package 中等.动态规划.背包;

/**
 * 给你一个整数数组 coins ，表示不同面额的硬币；以及一个整数 amount ，表示总金额。
 * 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额，返回 -1 。
 * 你可以认为每种硬币的数量是无限的。
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode.cn/problems/coin-change
 */
public class 零钱兑换_322 {

    public static void main(String[] args) {

        System.out.println(coinChange(new int[]{2}, 3));

    }

    /**
     * 动态规划
     * 子问题：
     * dp[i] 表示金额为 i 的最少硬币个数
     * 状态转移方程：
     * dp[i] = math.min(dp[i],dp[i-coin]+1)   (dp[i-coin]>0)
     * 注意：
     * 因为数组的默认值为 0 , 当 dp[i]=0 时，且 dp[i-coin]>0
     * dp[i] = dp[i-coin]+1
     *
     * @param coins
     * @param amount
     * @return
     */
    public static int coinChange(int[] coins, int amount) {
        if (amount == 0) {
            return 0;
        }
        int[] dp = new int[amount + 1];
        for (int i = 1; i <= amount; i++) {
            for (int coin : coins) {
                if (coin == i) {
                    dp[i] = 1;
                    continue;
                }
                // coin + amount-coin = amount
                // amount-coin 必须是能凑齐总金额的
                if (coin < i && dp[i - coin] > 0) {
                    if (dp[i] == 0) {
                        dp[i] = dp[i - coin] + 1;
                    } else {
                        dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                    }
                }
            }
        }
        return dp[amount] == 0 ? -1 : dp[amount];
    }

}
